package eclant.ant.types;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.resources.FileResource;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;

import eclant.eclipse.jdt.ClasspathVisitor;

public class ProjectsClasspathVisitor implements ClasspathVisitor
{
	private final ProjectComponent comp;
	private final List<Resource> files = new ArrayList<Resource>();
	private final boolean failOnError;
	private final boolean filePaths;
	private final boolean includeMain;
	private ContextNode context = null;

	public ProjectsClasspathVisitor(ProjectComponent logTo, boolean failOnError,
			boolean useFilePaths, boolean includeMain)
	{
		this.comp = logTo;
		this.failOnError = failOnError;
		this.filePaths = useFilePaths;
		this.includeMain = includeMain;
	}

	public List<Resource> results()
	{
		return Collections.unmodifiableList(files);
	}

	@Override
	public void externalLibrary(IPath name, boolean encountered)
	{
	}

	@Override
	public void library(IFolder classFolder, boolean encountered)
	{
	}

	@Override
	public void library(IFile jarFile, boolean encountered)
	{
	}

	@Override
	public boolean enter(IClasspathContainer container)
	{
		context = new ContextNode(null, context);
		return true;
	}

	@Override
	public boolean enter(IJavaProject project)
	{
		context = new ContextNode(project, context);
		return true;
	}

	@Override
	public void previouslyEncountered(IJavaProject project)
	{
	}

	@Override
	public void exit()
	{
		if (context.project != null)
			files.add(workspaceResource(context.project));
		context = context.parent;

		// remove last project if we are not including the main project
		if (context == null && !includeMain)
			files.remove(files.size() - 1);
	}

	@Override
	public void source(IJavaProject project, IFolder srcFolder, IFolder outputFolder,
			boolean encountered)
	{
	}

	@Override
	public void resolveFailed(IClasspathEntry entry)
	{
		String message = "Failed to resolve variable: " + entry.getPath().toPortableString();
		if (failOnError)
			throw new BuildException(message, comp.getLocation());
		else
			comp.log(message, Project.MSG_ERR);
	}

	@Override
	public void resolved(IClasspathEntry entry, IClasspathEntry resolved)
	{
	}

	private Resource workspaceResource(IJavaProject project)
	{
		if (filePaths)
		{
			IPath path = project.getProject().getLocation();
			return new FileResource(path.toFile(), path.toOSString());
		}
		else
		{
			String name = project.getElementName();
			return new Resource(name, true, project.getProject().getModificationStamp(), true);
		}
	}

	private static final class ContextNode
	{
		final IJavaProject project;
		final ContextNode parent;

		ContextNode(IJavaProject project, ContextNode parent)
		{
			this.project = project;
			this.parent = parent;
		}
	}
}
